package com.sd.chat;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.util.Date;
import java.util.logging.Level;

public class ServerMain extends Thread {

    private Logger log = new Logger(true);
    private ServerData db;
    private ServerManager manager;
    private TCPConReceiver tcpWaiter;
    private boolean shutdown = false;
    private ChatServerView serverView;
    private boolean iAmMain;
    private int timeoutsseguidos = 0;
    private String serverip = "127.0.0.1";
    private String backupserverip = "127.0.0.1";

    public ServerMain(String name, ChatServerView serverView, String adrr1, String adrr2, boolean isMain) {
        super(name);
        iAmMain = isMain;
        this.serverView = serverView;
        this.serverip = adrr1;
        this.backupserverip = adrr2;
    }

    @Override
    public void run() {


        if (iAmMain) {
            startUp(serverView);


            ServerPinger pingerServer = new ServerPinger(manager, ConfigsGerais.pingerclientport, ConfigsGerais.pingserverport, serverip);
            pingerServer.start();
            // TODO Auto-generated method stub
            System.out.println("run() ServerMain");
            if (iAmMain) {
                System.out.println("I AM MAIN");
            }
            manager.start();

            RMIServerTool h;
            try {
                h = new RMIServerTool(manager);
                Naming.rebind("rmi://"+serverip+"/chatserversd", h);
            } catch (RemoteException ex) {
                java.util.logging.Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex);
            } catch (MalformedURLException ex) {
                java.util.logging.Logger.getLogger(ClientManager.class.getName()).log(Level.SEVERE, null, ex);
            }

            tcpWaiter.start();
            while (!shutdown) {
                if (!manager.isAlive()) {
                    break;
                }
                if (!tcpWaiter.isAlive()) {
                    break;
                }

                manager.dispatchToGUI();
            }
        } else {
            startUp(serverView);


            while (!shutdown&&timeoutsseguidos<3) {
                updateBackup();
            }
                       
            
            // TODO Auto-generated method stub
            System.out.println("run() ServerMain");
            if (iAmMain) {
                System.out.println("I AM MAIN");
            }
            manager.start();

            RMIServerTool h;
            try {
                h = new RMIServerTool(manager);
                Naming.rebind("rmi://"+backupserverip+"/chatserversd", h);
            } catch (RemoteException ex) {
                java.util.logging.Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex);
            } catch (MalformedURLException ex) {
                java.util.logging.Logger.getLogger(ClientManager.class.getName()).log(Level.SEVERE, null, ex);
            }

            tcpWaiter.start();
            while (!shutdown) {
                if (!manager.isAlive()) {
                    break;
                }
                if (!tcpWaiter.isAlive()) {
                    break;
                }

                manager.dispatchToGUI();
            }
        }
    }

    public synchronized void updateBackup() {
        ServerPinger pingerBackup = new ServerPinger(manager, ConfigsGerais.pingserverport, ConfigsGerais.pingerclientport, serverip);
        pingerBackup.createSocket();
        pingerBackup.sendPing("ping ping");
        String reply = "";
        int bytesData = 4096;
        try {
            long timenow = new Date().getTime();
            reply = pingerBackup.receivePing();
            log.outln("ping receiveid: rtt=" + (new Date().getTime() - timenow));
            reply = reply.trim();
            try{
                bytesData = Integer.parseInt(reply);
                timeoutsseguidos = 0;
            }catch (NumberFormatException e){
                bytesData = 4096;
                timeoutsseguidos++;
            }
        } catch (SocketTimeoutException ex) {
            timeoutsseguidos++;
            log.erro("PINGER timeout");
        }        
        log.outln("reply size: " + reply);
        log.outln("timeouts: " + timeoutsseguidos);
        pingerBackup.sendBackupRequest("backup");
        try {
            ServerData backupStuff = pingerBackup.receiveBackup(bytesData);
            //log.outln("backup received: length="+Integer.parseInt(reply));
            if (backupStuff != null) {
                manager.setDb(backupStuff);
                log.outln("postsid: " + backupStuff.getPostID());
                log.outln("pmsid: " + backupStuff.getPmID());
            }
        } catch (SocketTimeoutException ex) {
            timeoutsseguidos++;
            log.erro("PINGER timeout");
        }
        try {
            log.outln("BACKUP preparing to wait");
            wait(5000);
        } catch (InterruptedException e) {
            log.erro("BACKUP interruptedException caught");
        }
        log.outln("BACKUP resuming");

    }

    public boolean isShutdown() {
        return shutdown;
    }

    public synchronized void setShutdown(boolean shutdown) {
        this.shutdown = shutdown;
        if (iAmMain) {
            shutDown();
        }
        notify();
    }

    private void startUp(ChatServerView serverView) {
        loadData();
        manager = new ServerManager(db, serverView);

        tcpWaiter = new TCPConReceiver(manager);

    }
    
    public ServerManager getManager(){
        return manager;
    }

    private void shutDown() {
        if (manager.isAlive()) {
            manager.shutdown();
        }
        if (tcpWaiter.isAlive()) {
            tcpWaiter.shutdown();
        }
        saveData();
    }

    private void loadData() {
        ObjectInputStream iS;
        boolean loaded = false;
        try {
            iS = new ObjectInputStream(new FileInputStream("CharServerDB"));

            db = (ServerData) iS.readObject();

            loaded = true;

        } catch (IOException e) {
            log.erro("abertura do ficheiro para load inicial");

        } catch (ClassNotFoundException e) {
        }

        if (!loaded) {
            db = new ServerData();
        }
    }

    public void saveData() {
        ObjectOutputStream oS;
        try {
            oS = new ObjectOutputStream(new FileOutputStream("CharServerDB"));

            oS.writeObject(db);
            log.outln("guardar para o ficheiro");
        } catch (IOException e) {
            log.erro("guardar para o ficheiro");

        }
    }
}
